//: src/com/silvtar/net/NetTransporter.java
/* 
 * Created by 朴晟宁 <piaosn@gmail.com> on 6/3/2013. 
 * Copyright (c) 2013 朴晟宁. All rights reserved.
 * 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program;
 * 
 * *********************************************************************
 * 
 * Modify History:
 * Date        Version    Description
 * 6/ 3/2013   0.1        Create The Class
 */

package com.silvtar.net.transporter;

import java.io.File;

import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import com.silvtar.io.StdOut;
import com.silvtar.io.StreamToolkit;
import com.silvtar.io.StreamTransporter;
import com.silvtar.net.Message;
import com.silvtar.net.NetPack;
import com.silvtar.net.Server;

/**
 * 内置文件传输类。工作良好。
 * 
 * @author 朴晟宁/Silvtar
 * @author piaosn@gmail.com
 * @version 0.1
 */
public class FileTransporter implements Server.Acceptor {

    static final int UPLOAD = 1;
    static final int DOWNLOAD = 2;
    
    static int threadNumber;
    
    Server      dataServer;
    InetAddress inetAddress_I;
    int         commandPort;
    int         dataPort;
    boolean     lock        = false;
    
    File        file;
    String      fileName;
    String      filePath;
    int status;

    // --------------------------------构造方法---------------------------------
    /**
     * 
     * @param inetAddress_S
     *            对方IP地址
     * @param commandPort
     *            命令通道端口
     * @param dataPort
     *            数据通道端口
     */
    public FileTransporter(String inetAddress_S, int commandPort, int dataPort) {

        try {
            inetAddress_I = InetAddress.getByName(inetAddress_S);
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        this.commandPort = commandPort;
        this.dataPort = dataPort;
    }

    // --------------------------------方法---------------------------------


    public String[] ls(String folder) {
        
        String[] fileNames = null;
        Socket cmdSocket = NetPack.connect(inetAddress_I, commandPort);
        StreamToolkit cmdST = new StreamToolkit(cmdSocket);
        cmdST.writeObject(new Message("ls", Message.COMMAND, folder));
        
        Message message = (Message) cmdST.readObject();
        if (message.getType() == Message.COMMANDRETURN) {
            if (message.getContent().equals("success")) {
                fileNames = (String[]) message.getAccessory();
            } 
            else if (message.getContent().equals("fail")) {
                fileNames = null;
            }
        }
        return fileNames;
    }
    
    /**
     * 开启数据传输服务器
     */
    public void startDataServer() {

        // 打开数据传输端口。
        if (dataServer == null) {
            dataServer = new Server(this, dataPort);
        }

        // 开始监听，等待接收方连接。
        if (!dataServer.isAlive()) {
            dataServer.setName("dataServer" + threadNumber++);
            dataServer.listen();
        }
        
    }

    /**
     * 上传目录
     * @param localPath 目录本地路径
     * @param remotePath 目录远程路径
     */
    public void uploadFolder(String localPath, String remotePath) {
        File localFolder = new File(localPath);
        if (localFolder.isFile()) {
            upload(localPath, remotePath);
            return;
        }
        else if (localFolder.isDirectory()){
            File[] files = localFolder.listFiles();
            for (int i = 0; i < files.length; i++) {
                String fileName = files[i].getName();
                uploadFolder(localPath + "/" + fileName, remotePath + "/" + fileName);
            }
        }
        else {
            StdOut.printLog("I don't know what's " + localPath);
        }
    }
    
    /**
     * 下载目录
     * @param remotePath 远程目录
     * @param localPath 本地目录
     */
    public void downloadFolder(String remotePath, String localPath) {
        
        String[] fileNames = ls(remotePath);
        
        if (fileNames == null) {
            download(remotePath, localPath);
        } 
        else {
            for (int i = 0; i < fileNames.length; i++) {
                downloadFolder(remotePath + "/" + fileNames[i], localPath  + "/" + fileNames[i]);
            }
        }
    }
    
    
    /**
     * 上传文件
     * @param localPath 文件本地路径
     * @param remotePath 文件远程路径
     */
    public void upload(String localPath, String remotePath) {
        
        file = new File(localPath);
        
        //检测文件是否为文件夹
        if (file.isDirectory()) {
            uploadFolder(localPath, remotePath);
            return;
        }
        
        // 检测文件是否存在
        if (!file.exists()) {
            StdOut.printErr("File " + localPath
                    + " don't exist! Cancel send file.");
            return;
        }

        status = UPLOAD;
        while (lock) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        // 获得路径
        fileName = file.getName();
        filePath = localPath;

        // 新建命令连接，并获得输出流。
        StreamToolkit st = new StreamToolkit(NetPack.connect(inetAddress_I,
                commandPort));
        
        //启动数据服务器
        startDataServer();
        
        st.writeObject(new Message(remotePath, Message.UPLOAD));
        
        //等待传输结果
        Message message = (Message) st.readObject();
        if (message.getType() == Message.RESULT) {
            if (message.getContent().equals("success")) {
                
            }
        }
    }

    /**
     * 网络发送文件。 暂时只能发送文件
     * 
     * @param localPath
     *            要传输的文件路径
     */
    public void download(String remotePath, String localPath) {
        
        file = new File(localPath);
        // 判断目录是否存在
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        
        status = DOWNLOAD;
        while (lock) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        // 获得路径
        fileName = file.getName();
        filePath = localPath;
        
        // 新建命令连接。
        StreamToolkit st = new StreamToolkit(NetPack.connect(inetAddress_I,
                commandPort));

        //启动数据服务器
        startDataServer();
        
        st.writeObject(new Message(remotePath, Message.DOWNLOAD));
        
        //等待传输结果
        Message message = (Message) st.readObject();
        if (message.getType() == Message.RESULT) {
            if (message.getContent().equals("success")) {
                
            }
            else {
                StdOut.printLog("文件不存在于服务器之上，下载失败");
            }
        }
    }

    /**
     * 会被服务器类回调的函数。当接收方通过数据传送连接连接到数据传送服务器时本类即被调用
     */
    @Override
    public void accept(int identifier) {
        lock = true;
        if (status == UPLOAD) {
            StreamTransporter.transStream(file, new StreamToolkit(dataServer,
                    identifier).getOutputStream());
        }
        else if (status == DOWNLOAD) {
            StreamTransporter.transStream(new StreamToolkit(dataServer,
                    identifier).getInputStream(), file);
        }
        
        lock = false;
    }
    
    public void closeDataServer() {
        if (dataServer != null && dataServer.isAlive()) {
            dataServer.close();
        }
    }
}
